﻿using JavaDevToys.Rcl.Models;
using JavaDevToys.Rcl.Utils;
using Scriban.Runtime;
using Scriban;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Force.DeepCloner;

namespace JavaDevToys.Rcl.Service.Impl
{
    public class CSharpCodeGenerator : ICSharpCodeGenerator
    {
        private IDirectoryPicker directoryPicker;

        public CSharpCodeGenerator(IDirectoryPicker directoryPicker)
        {
            this.directoryPicker = directoryPicker;
        }

        public List<string> GenerateBackModel(JavaProjectInfo javaProjectInfo, ApiInfo apiInfo)
        {
            List<string> pathList = new List<string>();

            // 生成请求类
            string requestClassPath = GenerateRequestClass(javaProjectInfo, apiInfo);
            pathList.Add(requestClassPath);

            // 生成响应类
            string responseClassPath = GenerateResponseClass(javaProjectInfo, apiInfo);
            pathList.Add(responseClassPath);

            return pathList;
        }

        public void GenerateBackDtoModel(JavaProjectInfo javaProjectInfo, ApiInfo _apiInfo, out List<string> newFileList, out Dictionary<string, string> fixCodeDict)
        {
            newFileList = new List<string>();
            fixCodeDict = new Dictionary<string, string>();
            foreach (var item in _apiInfo.DtoParameters)
            {
                //是否已经生成了dto
                if (directoryPicker.SearchFile(javaProjectInfo.FullPath, $"{item.Key}.cs", out string fullPath))
                {
                    //读取已经生成的dto
                    string templateContent = File.ReadAllText(fullPath);
                    var dtoParams = item.Value.DeepClone();
                    List<ParameterInfo> removeParams = new List<ParameterInfo>();
                    //移除已经生成的属性
                    foreach (var parameterInfo in dtoParams)
                    {
                        if (templateContent.Contains($" {parameterInfo.ParameterName};"))
                        {
                            removeParams.Add(parameterInfo);
                        }
                    }
                    foreach (var parameter in removeParams)
                    {
                        dtoParams.Remove(parameter);
                    }
                    if (dtoParams.Count > 0)
                    {
                        string newCode = GenerateDtoCode(dtoParams);
                        fixCodeDict.Add(fullPath, newCode);
                    }
                }
                else
                {
                    var filePathList = GenerateComplexClasses(javaProjectInfo, item.Key, _apiInfo.back_server_name, item.Value);
                    newFileList.AddRange(filePathList);
                }
            }
        }

        private string GenerateDtoCode(List<ParameterInfo> parameterInfos)
        {
            var context = new JavaTempData()
            {
                parameters = parameterInfos.Select(s => new JavaTempData_Parameter()
                {
                    data_type = s.DataType,
                    description = s.Description,
                    parameter_name = s.ParameterName,
                    required = s.IsRequired,
                }).ToList(),
            };

            string result = CreateCodeWithTemp("TemplateClass.Parameter.scriban", context);
            return result;
        }

        private List<string> GenerateComplexClasses(JavaProjectInfo javaProjectInfo, string className,string back_server_name, List<ParameterInfo> parameterInfos)
        {
            List<string> classPaths = new List<string>();
            string fileFullName = Path.Combine(
                    javaProjectInfo.RequestClassPath,
                    $"{className}.cs"
                );
            var context = new JavaTempData()
            {
                back_server_name = back_server_name,
                class_name = className,
                parameters = parameterInfos.Select(s => new JavaTempData_Parameter()
                {
                    data_type = s.DataType,
                    description = s.Description,
                    parameter_name = s.ParameterName,
                    required = s.IsRequired,
                }).ToList(),
            };

            string result = CreateCodeWithTemp("TemplateDtoClass.scriban", context);
            WriteJavaFile(fileFullName, result);
            classPaths.Add(fileFullName);
            return classPaths;
        }

        private string GenerateRequestClass(JavaProjectInfo javaProjectInfo, ApiInfo apiInfo)
        {
            string fileFullName = Path.Combine(
                javaProjectInfo.RequestClassPath,
                $"{JavaCodeHelper.ToUpperFirst(apiInfo.MethodName)}Request.cs"
            );

            var context = new JavaTempData()
            {
                back_server_name = apiInfo.back_server_name,
                class_name = apiInfo.MethodName,
                parameters = apiInfo.RequestParameters.Select(s => new JavaTempData_Parameter()
                {
                    data_type = s.DataType,
                    description = s.Description,
                    parameter_name = s.ParameterName,
                    required = s.IsRequired,
                }).ToList(),
                base_class = apiInfo.IsPaginated ? "BasePageRequest" : "BaseRequest",
            };

            string result = CreateCodeWithTemp("TempBackRequestModel.scriban", context);
            WriteJavaFile(fileFullName, result);
            return fileFullName;
        }

        private string GenerateResponseClass(JavaProjectInfo javaProjectInfo, ApiInfo apiInfo)
        {
            string fileFullName = Path.Combine(
                javaProjectInfo.ResponseClassPath,
                $"{JavaCodeHelper.ToUpperFirst(apiInfo.MethodName)}Response.cs"
            );

            var context = new JavaTempData()
            {
                back_server_name = apiInfo.back_server_name,
                class_name = apiInfo.MethodName,
                parameters = apiInfo.ResponseParameters.Select(s => new JavaTempData_Parameter()
                {
                    data_type = s.DataType,
                    description = s.Description,
                    parameter_name = s.ParameterName,
                    required = s.IsRequired,
                }).ToList(),
            };

            string result = CreateCodeWithTemp("TempBackResponseModel.scriban", context);
            WriteJavaFile(fileFullName, result);
            return fileFullName;
        }

        private void WriteJavaFile(string fileName, string content)
        {
            string filePath = Path.Combine(fileName);
            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(fileName));
            }
            File.WriteAllText(filePath, content, new UTF8Encoding(false));
            Console.WriteLine($"生成文件: {filePath}");
        }
        public Dictionary<string, string> GenerateBackController(JavaProjectInfo javaProjectInfo, List<JavaServiceInfo> javaServiceInfos)
        {
            return GenerateService(javaProjectInfo, javaServiceInfos, "TempBackController.scriban", p => p.ControllerPath);
        }

        public Dictionary<string, string> GenerateBackControllerMethod(JavaProjectInfo javaProjectInfo, List<JavaServiceInfo> javaServiceInfos)
        {
            return GenerateService(javaProjectInfo, javaServiceInfos, "TempBackControllerMethod.scriban", p => p.ControllerPath);
        }

        private Dictionary<string, string> GenerateService(JavaProjectInfo javaProjectInfo, List<JavaServiceInfo> javaServiceInfos, string tempName, Func<JavaProjectInfo, string> getFilePath)
        {
            return GenerateService(javaProjectInfo, javaServiceInfos, tempName, getFilePath.Invoke(javaProjectInfo));
        }
        private Dictionary<string, string> GenerateService(JavaProjectInfo javaProjectInfo, List<JavaServiceInfo> javaServiceInfos, string tempName, string filePath)
        {
            Dictionary<string, string> codeList = new Dictionary<string, string>();
            var classList = javaServiceInfos.Select(s => s.ClassName).Distinct().ToList();

            foreach (var item in classList)
            {
                var javaServiceList = javaServiceInfos.Where(w => w.ClassName == item).ToList();
                string className = string.Empty;
                if (tempName.Contains("Controller"))
                {
                    className = $"{item.Substring(0, 1).ToUpper() + item.Substring(1)}Controller";
                }
                
                var fullDirName = filePath;
                // 准备数据
                var modelData = new JavaTempData()
                {
                    back_server_name= javaServiceList.FirstOrDefault() ? .back_server_name,
                    class_name = item,
                    model_name = javaServiceList.FirstOrDefault()?.ModelName,
                    date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                    methods = javaServiceList.Select(m => new JavaTempData_Method()
                    {
                        code = m.Code,
                        name = m.Name,
                        mothedName = m.MothedName
                    }).ToList()
                };
                var javaCode = CreateCodeWithTemp(tempName, modelData);
                codeList.Add(className, javaCode);
            }

            return codeList;
        }

        /// <summary>
        /// 根据模板生成代码
        /// </summary>
        private string CreateCodeWithTemp(string templateName, object data)
        {
            var templatePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CSharpTemp", templateName);
            var templateContent = File.ReadAllText(templatePath);
            var template = Template.Parse(templateContent);
            // 创建 TemplateContext 并启用内置函数
            var context = new TemplateContext();
            var scriptObject = new ScriptObject();
            scriptObject.Import(data); // 将数据导入到模板
            context.PushGlobal(scriptObject);

            // 启用内置函数
            scriptObject.Import(typeof(JavaCodeHelper));

            var result = template.Render(context);
            return result;
        }
    }
}
